#include "XModel.h"
#include "Engine.h"
#include <string>

XModel::XModel() : 
m_numMaterials(0),
m_meshTextures(0),
m_meshMaterials(0),
m_normalTextures(0),
m_mesh(0)
{}
bool XModel::Load( const char* fileName, bool bumpMaps )
{
    /* Material buffer gets filled with the meshes material data from the file.
       We extract the data later in the function. */
    LPD3DXBUFFER materialBuffer;

    /* The name parameter needs to be null terminated (last char has to be 0) 
       I don't know why we work with pointers to shorts, it doesn't work with char* or int*. */
    short* name = TerminateEnd(fileName);
    
    /* Load the mesh from the specified file, casting the name to a 'long pointer to a char string' 
       This also gives us the location of the material data & the number of materials */
    HRESULT result = D3DXLoadMeshFromX((LPCWSTR)name, D3DXMESH_SYSTEMMEM,
                                        Engine::Instance()->GetDevice(), NULL,
                                        &materialBuffer, NULL, &m_numMaterials,
                                        &m_mesh );
    /* Tidy up memory */
    delete[] name;
                           
    /* exit early here if its all gone tits up */
    if(result != S_OK)
    {
        Release();
        return false;
    }

    /* Now we extract the material data */
    /* get a pointer to where the info is storred, and cast the pointer to a D3DXMATERIAL*  */
    D3DXMATERIAL* d3dxMaterials = ( D3DXMATERIAL* )materialBuffer->GetBufferPointer();

    /* Create a new array of material data, the size was given to us in the LoadMesh function above */
    m_meshMaterials = new D3DMATERIAL9[m_numMaterials];

    if( m_meshMaterials == NULL )
    {
        Release();
        return false;
    }

    //create a new array of texture data
    m_meshTextures = new LPDIRECT3DTEXTURE9[m_numMaterials];
    if(bumpMaps)
        m_normalTextures = new LPDIRECT3DTEXTURE9[m_numMaterials];

    if( m_meshTextures == NULL )
    {
        Release();
        return false;
    }

    //now we have our data allocated, we need to fill it up
    // for each material...
    for( DWORD i = 0; i < m_numMaterials; i++ )
    {
        // Copy the material
        m_meshMaterials[i] = d3dxMaterials[i].MatD3D;

        // Set the ambient color for the material (D3DX does not do this)
        m_meshMaterials[i].Ambient = m_meshMaterials[i].Diffuse;

        //null the pointer (we hate working with random data! )
        m_meshTextures[i] = NULL;

        //if the material has a texture name that is longer than 0
        if( d3dxMaterials[i].pTextureFilename != NULL &&
            lstrlenA( d3dxMaterials[i].pTextureFilename ) > 0 )
        {
            // Create the texture
            if( D3DXCreateTextureFromFileA( Engine::Instance()->GetDevice(),
                                            d3dxMaterials[i].pTextureFilename,
                                            &m_meshTextures[i] ) != S_OK)
            {
                //if creating it failed, exit 
                Release();
                return false;
            }
            if(bumpMaps)
            {
                std::string normalName = d3dxMaterials[i].pTextureFilename;
                int index = normalName.find('.');
                normalName.insert(index, "_bumpmap");
                // Create the normal map
                if( D3DXCreateTextureFromFileA( Engine::Instance()->GetDevice(), normalName.c_str(),
                    &m_normalTextures[i] ) != S_OK)
                {
                    //if creating it failed, exit 
                    Release();
                    return false;
                }
            }

        }
    }

    // Done with the material buffer
    materialBuffer->Release();

    return true;
}
void XModel::Render(ID3DXEffect* effect)
{
    UINT passes = 0;
    // Apply the technique contained in the effect 
    TEST(effect->Begin(&passes, 0));

    for( UINT pass = 0; pass < passes; ++pass )
    {
    
        TEST(effect->BeginPass( pass ));
        ////for each material
        for( DWORD i = 0; i < m_numMaterials; i++ )
        {
            TEST(effect->SetTexture("Diffuse", m_meshTextures[i]));
            if(m_normalTextures)
                TEST(effect->SetTexture("Normal", m_normalTextures[i]));
			TEST(effect->CommitChanges());
            //draw it
            TEST(m_mesh->DrawSubset( i )); 

        }
		TEST(effect->EndPass());
       
    }
    TEST(effect->End());
}
void XModel::Release()
{
    //delete the array of mesh materials
    if( m_meshMaterials != NULL )
        delete[] m_meshMaterials;

    //if we have any textures 
    if( m_meshTextures  || m_normalTextures)
    {
        //for each material
        for( DWORD i = 0; i < m_numMaterials; i++ )
        {
            //if this material had a texture, release it
            if( m_meshTextures[i] )
                m_meshTextures[i]->Release();
            if(m_normalTextures)
                if(m_normalTextures[i])
                    m_normalTextures[i]->Release();
        }
        //all the textures have been released, now call delete
        delete[] m_meshTextures;
        if(m_normalTextures)
            delete[] m_normalTextures;
    }

    //if we have a mesh pointer,
    if( m_mesh != NULL )
        m_mesh->Release();

    m_mesh				= NULL; 
    m_meshMaterials	    = NULL;
    m_meshTextures		= NULL; 
    m_normalTextures    = NULL;
    m_numMaterials	    = 0;
}
short* XModel::TerminateEnd( const char* fileName)
{
    /* make an array one character longer than needed*/
    short* target = new short[strlen(fileName)+1];
    /* copy the data across*/
    for(unsigned int i=0;i< strlen(fileName);i++)
    {
        target[i] = fileName[i];
    }

    /* set the last member to 0 */
    target[strlen(fileName)] = 0;

    return target;
}
